package com.opennews.openplatform

import com.opennews.openplatform.constants.RoleConstant
import grails.compiler.GrailsCompileStatic
import grails.gorm.transactions.Transactional
import groovy.transform.CompileDynamic
import org.hibernate.criterion.CriteriaSpecification

@Transactional(readOnly = true)
@GrailsCompileStatic
class RoleService {
    /**
     * Prepares the initial data for Role.
     */
    @Transactional
    void init() {
        if (Role.count() == 0) {
            // The system admin role.
            new Role().with {
                authority = RoleConstant.ROLE_SYSTEM_ADMIN
                title = "系统管理员"
                description = "当前整个系统的管理员，不属于任何账户。"
                save()
            }

            // The account group admin role.
            new Role().with {
                authority = RoleConstant.ROLE_ACCOUNT_GROUP_ADMIN
                title = "账户管理员"
                description = "可对当前账户组下的各项，如银行卡片和收支类别等进行管理。"
                save()
            }

            // The normal user role.
            new Role().with {
                authority = RoleConstant.ROLE_ACCOUNT_GROUP_USER
                title = "用户"
                description = "账户组普通用户"
                save()
            }

            // The normal user role.
            new Role().with {
                authority = RoleConstant.ROLE_ACCOUNT_GROUP_VIEWER
                title = "查看者"
                description = "账户组只读用户，仅可查看，不可操作数据。"
                save()
            }
        }
    }

    /**
     * Queries the role id by authority.
     * @param authority: The string of the authority. AKA role name.
     * @return
     */
    @CompileDynamic
    String queryId(String authority) {
        return Role.createCriteria().get {
            // Sets the query condition.
            eq("authority", authority)

            // Specifies the property we need.
            projections {
                property("id")
            }
        }.toString()
    }

    /**
     * Queries non system level roles with id and title.
     * @return The list of map instance which contains keys below.
     *          id: String of role id.
     *          authority: String of role authority. AKA role name.
     *          title: String of role title.
     *          description: String of role description.
     */
    @CompileDynamic
    List<Map<String, ?>> queryNonSystemLevelRoles() {
        // All system level roles which will be ignored from this result.
        def systemRoles = [
            RoleConstant.ROLE_SYSTEM_ADMIN
        ]

        return Role.createCriteria().list {
            // Transforms the result data format to Map instance.
            resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)

            not {'in'("authority", systemRoles)}

            // Specifies the property we need.
            projections {
                property("id", "id")
                property("authority", "authority")
                property("title", "title")
                property("description", "description")
            }
        } as List<Map<String, ?>>
    }

    /**
     * Queries all roles with id and title.
     * @return The list of map instance which contains keys below.
     *          id: String of role id.
     *          title: String of role title.
     */
    @CompileDynamic
    List<Map<String, ?>> queryAllRoles() {
        return Role.createCriteria().list {
            // Transforms the result data format to Map instance.
            resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)

            // Specifies the property we need.
            projections {
                property("id", "id")
                property("title", "title")
            }
        } as List<Map<String, ?>>
    }

    /**
     * Counts by role id list and role authority.
     * @param ids: String list of role ids.
     * @param authority: String of role authority.
     * @return Number of match.
     */
    @CompileDynamic
    int countByRoleIdsAndAuthority(List<String> ids, String authority) {
        Role.where {
            id in ids &&
                authority == authority
        }.count()
    }
}